package com.ncr.travel.appmgr.service.provider.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.ncr.travel.appmgr.model.shared.Product;
import com.ncr.travel.appmgr.model.shared.ProductCategory;
import com.ncr.travel.appmgr.model.shared.Screen;
import com.ncr.travel.appmgr.service.config.ConfigurationCache;
import com.ncr.travel.appmgr.service.converter.ScreenConfigurationConverter;
import com.ncr.travel.appmgr.service.model.ScreenConfiguration;
import com.ncr.travel.appmgr.service.provider.ProductsProvider;
import com.ncr.travel.appmgr.service.provider.ScreenConfigurationProvider;
import com.ncr.travel.tas.configuration.model.ConfigurationValue;
import com.ncr.travel.tas.shared.configuration.ConfigurationKey;
import com.ncr.travel.tas.shared.configuration.ConfigurationNodeEnum;
import com.ncr.travel.tas.shared.configuration.ConfigurationProductKey;

@Component
@Transactional(readOnly = true)
public class ScreenConfigurationProviderImpl implements ScreenConfigurationProvider {

	private static final Logger logger = LoggerFactory.getLogger(ScreenConfigurationProviderImpl.class);

	private final ConfigurationCache configurationCache;
	private final ProductsProvider productsProvider;
	private final ScreenConfigurationConverter screenConfigurationConverter;
	private Map<String, ScreenConfiguration> screenConfigCache = Collections.synchronizedMap(new HashMap<String, ScreenConfiguration>());

	@Autowired
	public ScreenConfigurationProviderImpl(ConfigurationCache configurationCache, ProductsProvider productsProvider, ScreenConfigurationConverter screenConfigurationConverter) {
		super();
		this.configurationCache = configurationCache;
		this.productsProvider = productsProvider;
		this.screenConfigurationConverter = screenConfigurationConverter;
	}

	@PostConstruct
	public void loadScreenConfiguration() throws Exception {
		List<Product> products = productsProvider.getProducts("");
		for (Product product : products) {
			for (ProductCategory category : product.getCategories()) {
				if (category.getScreens() != null) {
					for (Screen screen : category.getScreens()) {
						StringBuffer path = new StringBuffer().append(category.getKey()).append("&").append(screen.getKey());
						logger.debug("path = '" + path + "'");
						ScreenConfiguration screenConfiguration = findScreenConfigurationByKeyAndPath(product.getKey(), path.toString());
						String cacheKey = path.insert(0, product.getKey() + "/").toString();
						logger.debug("cacheKey = '" + cacheKey + "'");
						screenConfigCache.put(cacheKey, screenConfiguration);
						if (!screen.getChildScreens().isEmpty()) {
							for (Screen childScreen : screen.getChildScreens()) {
								StringBuffer childPath = new StringBuffer().append(category.getKey()).append("&").append(screen.getKey()).append("&").append(childScreen.getKey());
								logger.debug("childPath = '" + childPath + "'");
								ScreenConfiguration childScreenConfiguration = findScreenConfigurationByKeyAndPath(product.getKey(), childPath.toString());
								cacheKey = childPath.insert(0, product.getKey() + "/").toString();
								logger.debug("cacheKey = '" + cacheKey + "'");
								screenConfigCache.put(cacheKey, childScreenConfiguration);
							}
						}
					}
				}
			}
		}
	}

	/**
	 * @see com.ncr.travel.appmgr.service.provider.ScreenConfigurationProvider#findScreenConfigurationByKeyAndPath(java.lang.String,
	 *      java.lang.String)
	 */
	@Override
	public ScreenConfiguration findScreenConfigurationByKeyAndPath(String key, String path) throws Exception {
		String lookupKey = key + "/" + path;
		if (screenConfigCache.containsKey(lookupKey)) {
			return screenConfigCache.get(lookupKey);
		}

		Set<ConfigurationValue> productConfigurations = configurationCache.getConfigurationValues(ConfigurationNodeEnum.APPMGR, ConfigurationProductKey.APPLICATION_MANAGER_UI, ConfigurationKey.PRODUCTS);

		// get all products
		ConfigurationValue products = productConfigurations.toArray(new ConfigurationValue[productConfigurations.size()])[0];
		// find product by its key
		ConfigurationValue productConfig = products.findChild(key);
		ConfigurationValue configValue = productConfig;
		// get path tokens for traversing config values
		String[] tokens = StringUtils.tokenizeToStringArray(path, "&");

		// search for config value based on path tokens
		for (String token : tokens) {
			configValue = findChild(configValue, token);
		}

		// create screen config from config value
		ScreenConfiguration screenConfiguration = screenConfigurationConverter.toScreenConfiguration(configValue);
		screenConfiguration.setKey(lookupKey);

		return screenConfiguration;
	}

	private ConfigurationValue findChild(ConfigurationValue config, String node) {
		return config.findChild(node);
	}

	/**
	 * @see com.ncr.travel.appmgr.service.provider.com.ncr.travel.appmgr.service.provider.ScreenConfigurationProvider#save(com.ncr.travel.appmgr.model.ScreenConfiguration)
	 */
	@Override
	@Transactional
	public void save(ScreenConfiguration screenConfiguration, String path) throws Exception {

		logger.debug("In ScreenConfiguratonProviderImpl.save() path = '" + path + "'");
		ScreenConfiguration cachedScreenConfig = null;
		if (screenConfigCache.containsKey(path)) {
			cachedScreenConfig = screenConfigCache.get(path);
		}

		if (screenConfiguration.getText() != null) {
			if (cachedScreenConfig != null) {
				cachedScreenConfig.setText(screenConfiguration.getText());
			}
			// get a list of configurationValues that need to be updated
			List<ConfigurationValue> values = screenConfigurationConverter.convertToTextConfigValues(screenConfiguration);
			if (values != null) {
				for (ConfigurationValue textConfigValue : values) {
					configurationCache.setConfigurationValue(textConfigValue, ConfigurationKey.TEXT);
				}
			}
		}

		if (screenConfiguration.getFunctions() != null) {
			if (cachedScreenConfig != null) {
				cachedScreenConfig.setFunctions(screenConfiguration.getFunctions());
			}
			// get a list of configurationValues that need to be updated
			List<ConfigurationValue> values = screenConfigurationConverter.convertToFunctionConfigValues(screenConfiguration);
			if (values != null) {
				for (ConfigurationValue functionConfigValue : values) {
					configurationCache.setConfigurationValue(functionConfigValue, ConfigurationKey.FUNCTIONS);
				}
			}
		}

		if (screenConfiguration.getRules() != null) {
			if (cachedScreenConfig != null) {
				cachedScreenConfig.setRules(screenConfiguration.getRules());
			}
			// get a list of configurationValues that need to be updated
			List<ConfigurationValue> values = screenConfigurationConverter.convertToRuleConfigValues(screenConfiguration);
			if (values != null) {
				for (ConfigurationValue ruleConfigValue : values) {
					configurationCache.setConfigurationValue(ruleConfigValue, ConfigurationKey.RULES);
				}
			}
		}

	}

}
